using System;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
using AdRotatorXNA.Helpers;
using Microsoft.Xna.Framework.Input;
using System.Collections.Generic;

#if WINDOWS_RT
using Windows.Storage;
using System.Threading.Tasks;
#else
using System.IO.IsolatedStorage;
#endif

namespace AdRotatorXNA
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    internal class AdRotatorDefaultHouseAd : AdComponent
    {
        private static AdRotatorDefaultHouseAd instance;

        private static Texture2D _currentTexture;
        
        private Vector2 adPosition = Vector2.Zero;

        private Rectangle BannerRect = Rectangle.Empty;

        private bool Initialised = false;

        MouseState _prevMouseState;

        private Vector2 AdSize;

        private static Dictionary<string, Texture2D> _loadedTextures = new Dictionary<string,Texture2D>();

        private bool _clicked = false;
        private bool Clicked
        {
            get { return _clicked; }
            set 
            {
                if (value == _clicked)
                    return;

                _clicked = value;

                if (_clicked)
                    Clicked();

            }
        }

        private AdRotatorDefaultHouseAd(Game game)
            : base(game)
        {
            // TODO: Construct any child components here
        }


        public static AdRotatorDefaultHouseAd Current
        {
            get
            {
                if (AdRotatorXNAComponent.game == null)
                    return null;

                if (instance == null)
                    instance = new AdRotatorDefaultHouseAd(AdRotatorXNAComponent.game);

                return instance;
            }
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            if (Initialised)
                return;

            // TODO: Add your initialization code here
            Initialised = true;
            
            base.Initialize();
        }

        public static void Initialize(Game _game, string houseAdRemoteURL)
        {
            Current.Initialize();

            Current.GetImage(houseAdRemoteURL);
        }

        // Choose image
        private void GetImage(string file)
        {
            var fileName = Path.GetFileName(new Uri(file).AbsolutePath);

            // see if we have this image cached.
            if (_loadedTextures.TryGetValue(fileName, out _currentTexture))
            {
                State = AdState.AdLoaded;
                return;
            }

            // try to load it.
            GetRemoteImageFromURL(file);
        }

        public override void UpdateAd(AdRotator.Model.AdInformation adInfo)
        {
            AdInfo = adInfo;
            
            GetImage(adInfo.AppID);
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here

            if (Clicked)
            {
                Clicked = false;
                _prevMouseState = Mouse.GetState();
                return;
            }

            var currentMouseState = Mouse.GetState();

            //TODO: Better refactor of touch/mouse input logic
            var touchState = AdRotatorXNAComponent.Current.TouchState;
            if (touchState.Count > 0 && !Clicked)
            {
                // Find a valid touch event.
                // NOTE: We don't use gestures here because Tap may or may not be
                // enabled in the application.
                foreach (var touch in touchState)
                {
                    if(touch.State != TouchLocationState.Released)
                        continue;

                    TouchLocation lastTouch;

                    if (!touch.TryGetPreviousLocation(out lastTouch))
                        continue;

                    if (BannerRect.Contains(new Point((int)touch.Position.X, (int)touch.Position.Y)))
                        Clicked = true;
                }

            }

            if (_prevMouseState.LeftButton == ButtonState.Pressed && !Clicked)
            {
                if (currentMouseState.LeftButton != _prevMouseState.LeftButton)
                {
                    if (BannerRect.Contains(new Point((int)currentMouseState.X, (int)currentMouseState.Y)))
                        Clicked = true;
                }
            }
            _prevMouseState = currentMouseState;

            base.Update(gameTime);
        }

        public override void UpdatePosition(Vector2 newposition)
        {
            adPosition = newposition;

            if (Initialised)
                UpdateBanner();
        }

        private void UpdateBanner()
        {

            if (_currentTexture == null)
                return;

            BannerRect = new Rectangle((int)adPosition.X, (int)adPosition.Y, AdInfo.Width, AdInfo.Height);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            if (_currentTexture == null)
                return;

            var spriteBatch = AdRotatorXNAComponent.Current.spriteBatch;
            // TODO: Add your drawing code here
            spriteBatch.Begin();

            spriteBatch.Draw(_currentTexture, BannerRect, Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }

        void GetRemoteImageFromURL(string url)
        {
            var fileName = Path.GetFileName(new Uri(url).AbsolutePath);

            ImageDownload.GetImageFromURL(GraphicsDevice, url, (s, e) =>
            {
                if (e != null)
                {
                    LoadRemoteImageFromISO(fileName);
#if WINDOWS_RT
                    System.Diagnostics.Debug.WriteLine(e.Message);
#else
                    Console.WriteLine(e.Message);
#endif    
                }
                else
                {
                    _currentTexture = s;

                    if (!_loadedTextures.ContainsKey(fileName))
                        _loadedTextures.Add(fileName, s);

                    SaveRemoteImagetoISO(fileName);
                    UpdateBanner();

                    State = AdState.AdLoaded;

                }
            });
        }

        void SaveRemoteImagetoISO(string fileName)
        {
            if (_currentTexture == null)
                return;

            try
            {
#if WINDOWS_RT
                Task.Run(async () => 
                {
                    using (var fileStream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync(fileName, CreationCollisionOption.ReplaceExisting))
                    {
                        _currentTexture.SaveAsPng(fileStream, _currentTexture.Width, _currentTexture.Height);
                    }
                });
                    
#else
                using (IsolatedStorageFileStream isfStream = new IsolatedStorageFileStream(fileName, FileMode.Create, IsolatedStorageFile.GetUserStoreForApplication()))
                {
                    _currentTexture.SaveAsPng(isfStream, _currentTexture.Width, _currentTexture.Height);
                }
#endif

            }
            catch
            {

            }
        }

        void LoadRemoteImageFromISO(string fileName)
        {
            try
            {

#if WINDOWS_RT
                Task.Run(async () =>
                {
                    try
                    {
                        using (var fileStream = await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync(fileName))
                        {
                            _currentTexture = Texture2D.FromStream(this.GraphicsDevice, fileStream);

                            if (!_loadedTextures.ContainsKey(fileName))
                                _loadedTextures.Add(fileName, _currentTexture);

                        }

                        UpdateBanner();

                        State  = AdState.AdLoaded;
                    }
                    catch
                    {
                        State = AdState.AdFailed;
                    }

                });
#else
                using (IsolatedStorageFileStream isfStream = IsolatedStorageFile.GetUserStoreForApplication().OpenFile(fileName, FileMode.Open))
                {
                    _currentTexture = Texture2D.FromStream(this.GraphicsDevice, isfStream);
                }

                State = AdState.AdLoaded;
#endif
            }
            catch
            {
                State = AdState.AdFailed;
            }
        }
    }
}
